﻿//-----------------------------------------------------------------------------
// File: SettingsFactory.cs
// Author: Pierre Biermann (tauron031@gmail.com)
// Project: FFMPEG audio video converter class library
// Namespace(s): FFShellAPI.FFMPEG
//
// Desc: Settings factory class.
//       
// (c) 2007 - 2013 by Pierre Biermann
//-----------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.IO;

namespace FFShellAPI.FFMPEG.Settings
{
    /// <summary>
    /// Class that contains screen resolution data.
    /// </summary>
    public class Resolution
    {
        /// <summary>
        /// Create a new Resolution instance.
        /// </summary>
        public Resolution()
        {
            this.Name = "vga";
            this.Width = 640;
            this.Height = 480;
        }

        /// <summary>
        /// Create a new Resolution instance.
        /// </summary>
        /// <param name="name">name of the resolution</param>
        /// <param name="width">pixel width</param>
        /// <param name="height">pixel height</param>
        public Resolution(string name, int width, int height)
        {
            this.Name = name;
            this.Width = width;
            this.Height = height;
        }

        /// <summary>
        /// Creates a resolution instance from xml
        /// </summary>
        /// <param name="xml">xml root node for resolution data</param>
        public Resolution(XElement xml)
        {
            if (xml.Name.LocalName.Equals("resolution"))
            {
                XAttribute attr = xml.Attribute("name");
                if (attr != null)
                {
                    Name = attr.Value;
                }
                attr = xml.Attribute("width");
                if (attr != null)
                {
                    Width = Int32.Parse(attr.Value);
                }
                attr = xml.Attribute("height");
                if (attr != null)
                {
                    Height = Int32.Parse(attr.Value);
                }
            }
        }

        /// <summary>
        /// Return the hash code for the resolution.
        /// </summary>
        /// <returns>hash code</returns>
        public override int GetHashCode()
        {
            return Width * Height;
        }

        /// <summary>
        /// Returns the resolution as string.
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return String.Format("{0}x{1} ({2})", Width, Height, Name); 
        }

        /// <summary>
        /// Apply the current resolution to a VideoSettings instance.
        /// </summary>
        /// <param name="video">VideoSettings instance</param>
        public void Apply(VideoSettings video)
        {
            if (video != null)
            {
                video.PixelWidth = Width;
                video.PixelHeight = Height;
            }
        }

        /// <summary>
        /// Get or sets the name for the resolution.
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// Gets or sets the pixel width.
        /// </summary>
        public int Width { get; set; }

        /// <summary>
        /// Gets or sets the pixel height.
        /// </summary>
        public int Height { get; set; }
    }

    /// <summary>
    /// Class that represents a single audio and video container format with subformat description.
    /// </summary>
    public class FormatPreset
    {
        const string fmtKey = "format";
        const string astrmKey = "acodec";
        const string vstrmKey = "vcodec";
        const string extKey = "ext";
        const string descKey = "desc";
        const string codecKey = "codec";

        /// <summary>
        /// Creates a new FormatPreset instance.
        /// </summary>
        public FormatPreset()
        {
            AudioCodecName = String.Empty;
            VideoCodecName = String.Empty;
            FormatDescription = "Unknown audio and video format";
            FormatExtension = "bin";
            SupportVideo = true;
            SupportAudio = true;
        }

        public FormatPreset(XElement e) : this()
        {
            if (e.Name.LocalName.Equals(fmtKey))
            {
                if (e.Attribute(extKey) != null)
                {
                    FormatExtension = e.Attribute(extKey).Value;
                }
                if (e.Attribute(descKey) != null)
                {
                    FormatDescription = e.Attribute(descKey).Value;
                }
                if (e.Attribute(astrmKey) != null)
                {
                    AudioCodecName = e.Attribute(astrmKey).Value;
                }
                if (e.Attribute(vstrmKey) != null)
                {
                    VideoCodecName = e.Attribute(vstrmKey).Value;
                }
            }
        }

        /// <summary>
        /// Applying the settings to a ConversionSettings instance.
        /// </summary>
        /// <param name="settings"> ConversionSettings instance</param>
        public void Apply(ConversionSettings settings)
        {
            if (settings != null)
            {
                if (!String.IsNullOrWhiteSpace(AudioCodecName))
                    settings.AudioSettings.CodecName = AudioCodecName;
                if (!String.IsNullOrWhiteSpace(VideoCodecName))
                    settings.VideoSettings.CodecName = VideoCodecName;
            }
        }

        /// <summary>
        /// Applying the settings to a AudioSettings instance.
        /// </summary>
        /// <param name="settings"> AudioSettings instance</param>
        public void Apply(AudioSettings settings)
        {
            if (settings != null)
            {
                if (!String.IsNullOrWhiteSpace(AudioCodecName))
                    settings.CodecName = AudioCodecName;
            }
        }

        /// <summary>
        /// Applying the settings to a VideoSettings instance.
        /// </summary>
        /// <param name="settings"> VideoSettings instance</param>
        public void Apply(VideoSettings settings)
        {
            if (settings != null)
            {
                if (!String.IsNullOrWhiteSpace(VideoCodecName))
                    settings.CodecName = VideoCodecName;
            }
        }

        /// <summary>
        /// Swaps the file extension against the container file extension.
        /// </summary>
        /// <param name="originalName">file name to change</param>
        /// <returns>file name with new extension</returns>
        public string ReplaceFileExt(string originalName)
        {
            string ext = String.Format(".{0}", FormatExtension);
            FileInfo finfo = new FileInfo(originalName);
            if (!String.IsNullOrEmpty(finfo.Extension))
                return originalName.Replace(finfo.Extension, ext);
            else
                return originalName + ext;
        }

        /// <summary>
        /// Returns the instance as a string.
        /// </summary>
        /// <returns>object string</returns>
        public override string ToString()
        {
            return String.Format("{0} (.{1})", FormatDescription, FormatExtension);
        }

        /// <summary>
        /// Get or sets the name of the audio codec for the audio sub stream.
        /// </summary>
        public string AudioCodecName { get; set; }

        /// <summary>
        /// Get or sets the name of the video codec for the video sub stream.
        /// </summary>
        public string VideoCodecName { get; set; }

        /// <summary>
        /// Gets or sets the container format description.
        /// </summary>
        public string FormatDescription { get; set; }

        /// <summary>
        /// Gets or sets the format file extension.
        /// </summary>
        public string FormatExtension { get; set; }

        /// <summary>
        /// Enables or disables video support for the current format preset.
        /// </summary>
        public bool SupportVideo { get; set; }

        /// <summary>
        /// Enables or disables audio support for the current format preset.
        /// </summary>
        public bool SupportAudio { get; set; }
    }

    /// <summary>
    /// XML loader class for A/V format presets.
    /// </summary>
    public class PresetLoader
    {
        List<FormatPreset> presets;
        List<Resolution> resolutions;

        const string resRootKey = "resolutions";
        const string avRootKey = "av-format-list";
        const string aRootKey = "audio-format-list";

        /// <summary>
        /// Creates a new FormatPresets instance and load the content from the given xml file.
        /// </summary>
        /// <param name="root">XML root key</param>
        public PresetLoader(XElement root)
        {
            XElement avroot = root.Element(avRootKey);
            presets = new List<FormatPreset>();
            if (avroot != null)
            {
                foreach (var e in avroot.Elements())
                {
                    presets.Add(new FormatPreset(e));
                }
            }
            XElement aroot = root.Element(aRootKey);
            if (aroot != null)
            {
                foreach (var e in aroot.Elements())
                {
                    FormatPreset preset = new FormatPreset(e);
                    preset.SupportVideo = false;
                    presets.Add(preset);
                }
            }
            resolutions = new List<Resolution>();
            XElement resroot = root.Element(resRootKey);
            if (resroot != null)
            {
                foreach (var e in resroot.Elements())
                {
                    resolutions.Add(new Resolution(e));
                }
            }
        }

        /// <summary>
        /// Returns an array with all format presets that has been loaded 
        /// from the xml source file by the class constructor.
        /// </summary>
        public FormatPreset[] Presets
        {
            get { return presets.ToArray(); }
        }

        /// <summary>
        /// Returns an array with all resolution presets that has been loaded 
        /// from the xml source file by the class constructor.
        /// </summary>
        public Resolution[] Resolutions
        {
            get 
            { 
                var sorted = from s in resolutions orderby s.GetHashCode() select s;
                return sorted.ToArray(); 
            }
        }
    }
}
